/*
 ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
 2011,2012 Giovanni Di Sirio.

 This file is part of ChibiOS/RT.

 ChibiOS/RT is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version.

 ChibiOS/RT is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.

 ---

 A special exception to the GPL can be applied should you wish to distribute
 a combined work that includes ChibiOS/RT, without being obliged to provide
 the source code for any proprietary components. See the file exception.txt
 for full details of how and when the exception can be applied.
 */

#include "main.h"
#include "myusart.h"

#include "test.h"
#include "rficonsole.h"
#include "lis302dl.h"
#include "chprintf.h"
#include "drawing.h"
#include "denso_adc.h"
#include "denso_adc_io.h"
#include "touchpad.h"
#include "osc_io.h"
#include "rfirtc.h"

#include "stm32f4xx_syscfg.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_exti.h"

#include "time.h"

//#include "gdisp.h"
#include "lcd_SSD1289.h"

#include "distributor_emulator.h"
#include "io.h"

#include "rfifile.h"
#include "osc_state.h"

//static void spicb(SPIDriver *spip);

/*
 * SPI1 configuration structure.
 * Speed 5.25MHz, CPHA=1, CPOL=1, 8bits frames, MSb transmitted first.
 * The slave select line is the pin GPIOE_CS_SPI on the port GPIOE.
 */
//static const SPIConfig spi1cfg = { NULL,
///* HW dependent part.*/GPIOE, GPIOE_CS_SPI, SPI_CR1_BR_0 | SPI_CR1_BR_1
//| SPI_CR1_CPOL | SPI_CR1_CPHA };
/*
 * SPI2 configuration structure.
 * Speed 21MHz, CPHA=0, CPOL=0, 16bits frames, MSb transmitted first.
 * The slave select line is the pin 12 on the port GPIOA.
 */
//static const SPIConfig spi2cfg = { spicb,
///* HW dependent part.*/GPIOB, 12, SPI_CR1_DFF };
/*
 * PWM cyclic callback.
 * A new ADC conversion is started.
 */

/*
 * SPI end transfer callback.
 */
//static void spicb(SPIDriver *spip) {
//
//	/* On transfer end just releases the slave select line.*/
//	chSysLockFromIsr()
//	;
//	spiUnselectI(spip);
//	chSysUnlockFromIsr();
//}
//
static Mutex mtx_lcd;

/*
 * This is a periodic thread that does absolutely nothing except flashing
 * a LED.
 */
static WORKING_AREA(waThread1, 128);
static msg_t blinkingThreadEntryPoint(void *arg) {

	(void) arg;
	chRegSetThreadName("blinker");
	int counter = 0;
	while (TRUE) {

		chThdSleepMilliseconds(1000);

		chMtxLock(&mtx_lcd);
		// white does not work
		LCD_SetTextColor(
				(counter++ % 2) == 0 ? LCD_COLOR_YELLOW : LCD_COLOR_BLACK);
		//LCD_SetTextColor(LCD_COLOR_GREEN);
		LCD_DrawFullCircle(10, 10, 10);
		chMtxUnlock();
	}
	return 0;
}

void drawBar(int fromX, int toX, int baseY, int length, int value) {
	int i;
	for (i = fromX; i < toX; i++) {
		LCD_SetTextColor(LCD_COLOR_RED);
		LCD_DrawLine(baseY, 320 - i, value, LCD_DIR_VERTICAL);

		LCD_SetTextColor(LCD_COLOR_GREEN);
		LCD_DrawLine(baseY + value, 320 - i, length - value, LCD_DIR_VERTICAL);
	}
}

time_t start_time;
int revol = 0;

int adcZoom = 1;

void displayValue(uint16_t xCoord, uint16_t yCoord, char *ptr, int value);


void displayOscData(adc_snapshot *data) {
	int i;
	// fill rectangle?
//	for (i = 0; i < 320; i++)
	//LCD_DrawLine(70, 320 - i, 240 - 70, LCD_DIR_VERTICAL);

	LCD_SetTextColor(LCD_COLOR_BLACK);

	displayValue(13, 7, "count", data->tail);

	int last_index = data->tail - 1;

	if (last_index < 1) {
		drawBar(0, LCD_PIXEL_WIDTH - 1, 100, 139, 0);
	} else {
		time_t startTime = data->states[0].time;
		time_t totalTime = data->states[last_index].time - startTime;

		for (i = 0; i < LCD_PIXEL_WIDTH; i++) {

#define BAR_TIME(x) ((totalTime * (x) / LCD_PIXEL_WIDTH / adcZoom) + startTime)

			//time_t barTime = (totalTime * i / 320 / adcZoom) + startTime;
			time_t barTime = BAR_TIME(i);

			int index = findIndexForTime(barTime, data);
			if (index < 0) {
				displayValue(13, 10, "error", index);
				break;
			}

			int nextIndex = index;
			if (i != LCD_PIXEL_WIDTH - 1) {
				time_t nextBarTime = BAR_TIME(i + 1);
				nextIndex = findIndexForTime(nextBarTime, data);
			}

			int k;
#define BAR_W 140
#define EACH_BAR (BAR_W / ADC_NUMBER_CHANNELS)

			adc_state *state = &data->states[index];

			for (k = 0; k < ADC_NUMBER_CHANNELS; k++) {
				int isOn = isHigh(state->adc_data[k]);

				int isChanging = FALSE;
				if (index != nextIndex) {
					adc_state *nextState = &data->states[nextIndex];
					isChanging = isOn != isHigh(nextState->adc_data[k]);
				}

				int value;
				if (isChanging) {
					// there are events inside the bar - drawing half size
					value = EACH_BAR / 2;
				} else {
					value = isOn ? 5 : EACH_BAR - 5;
				}

				drawBar(i - 1, i, 100 + k * EACH_BAR,
						BAR_W / ADC_NUMBER_CHANNELS, value);
			}

		}

		displayValue(0, 7, "time", totalTime);
	}
}

int fps_counter = 0;

void clearReport() {
	print("clearReport\r\n");
	print("de revolution period: %d\r\n", getRevolutionPeriod());

	LCD_SetTextColor(LCD_COLOR_BLACK);
	start_time = currentTimeMillis();
	revol = 0;
	fps_counter = 0;
	// fill rectangle?
	for (int i = 0; i < 320; i++)
		LCD_DrawLine(70, 320 - i, 240 - 70, LCD_DIR_VERTICAL);
	resetSnapshot();
}

#define STATE_INITIAL 0
#define STATE_RUNNING 1

void initExtI() {
	// configure lines as input PA1 PA3
	palSetGroupMode(IOPORT1, PAL_PORT_BIT(1) | PAL_PORT_BIT(3), 0,
			PAL_MODE_INPUT);

	EXTILine_Config(GPIO_Pin_0, EXTI_PinSource0, EXTI_Line0, EXTI0_IRQn);
	EXTILine_Config(GPIO_Pin_1, EXTI_PinSource1, EXTI_Line1, EXTI1_IRQn);
//	EXTILine_Config(GPIO_Pin_3, EXTI_PinSource3, EXTI_Line3, EXTI3_IRQn);

	// enable EXTI1 & 3 interrupt
	nvicEnableVector(EXTI0_IRQn, CORTEX_PRIORITY_MASK(7));
	nvicEnableVector(EXTI1_IRQn, CORTEX_PRIORITY_MASK(7));
//	nvicEnableVector(EXTI3_IRQn, CORTEX_PRIORITY_MASK(7));

}

static MemoryHeap heap1;

static WORKING_AREA(HEAP1, 60000);

static adc_snapshot osc_data;

volatile int adcRefreshTime = 10;

void printOscStats() {
	chMtxLock(&mtx_lcd);
	printOscData(&osc_data);
	chMtxUnlock();
}

extern volatile int adc_callback_counter;

int fatalError = FALSE;

void myassert(int condition, char message[]) {
	if (!condition) {
		chMtxLock(&mtx_lcd);
		fatalError = TRUE;
		LCD_Clear(LCD_COLOR_ORANGE);
		LCD_DisplayStringLine(0, 0, message);
		chMtxUnlock();
	}
}

void printADC(int y, int adcValue, char *text) {
	int value = adcValue * 3000 / 4096;
	int boolean = !isHigh(value);
	LCD_SetTextColor(boolean ? LCD_COLOR_RED : LCD_COLOR_GREEN);
	displayValue(0, y, text, value);
}

time_t reset_time = 0;

int main(void) {
	halInit();
	chSysInit();

	initUsart();

	initDistributorEmulator();

//	initSt7735();

//	lcdInit();
	//lcdTest();


//	initExtI(); // PA1 PA3

	initFileUtil();

//	TOUCHPADDriver TOUCHPADD1 = {
//		&SPID1,
//	};

	chMtxInit(&mtx_lcd); /* Mutex initialization before use */

	initMyAdc();
	initDensoAdc2();
	initDensoAdc();
	initOscState();

	LCD_SSD1289_Init();
	LCD_Clear(LCD_COLOR_RED);

	LCD_SetFont(&LCD_DEFAULT_FONT);


	LCD_SetFont(&Font12x12);
	LCD_SetTextColor(LCD_COLOR_WHITE);
	// for some reason text disappears if BakcColor is BLACk
	LCD_SetBackColor(LCD_COLOR_BLUE2);
	LCD_DisplayStringLine(0, 0, VERSION_STRING);

	/*
	 * Creates the example thread.
	 */
	chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO,
			blinkingThreadEntryPoint, NULL );

	clearReport();
	clearSnapshot(&osc_data);

	while (!fatalError) {
		time_t now = currentTimeMillis();

		chMtxLock(&mtx_lcd);
		copySnapshot(&osc_data);

		displayOscData(&osc_data);

		if (reset_time > 0 && now > reset_time) {
			clearReport();
			reset_time = 0;
		}

		if (osc_data.tail == getAdcSamplesCount() && reset_time == 0
				&& adcRefreshTime > 0) {
			reset_time = now + adcRefreshTime * SECOND;
		}

		LCD_SetBackColor(LCD_COLOR_BLUE2);
		LCD_SetTextColor(LCD_COLOR_BLACK);

#define PERIOD 20000

		fps_counter++;
		long time = now - start_time;

		int timeSeconds = time / 1000;
		displayValue(13, 2, "rtime", timeSeconds);
		displayValue(13, 3, "a c c", adc_callback_counter);
		displayValue(13, 4, "revol", revol);
		if (time > 0) {
			displayValue(13, 5, "100f", 100 * fps_counter / timeSeconds);
		}

		printADC(1, getAdc0(), "ADC0");
		printADC(2, getAdc1(), "ADC1");
		printADC(3, getAdc2(), "ADC2");
		printADC(4, getAdc3(), "ADC3");

//		pri(0, 3, "ext int", externalInterrupt);

		chMtxUnlock();

//		chThdSleepMilliseconds(50);
//		runTest();
	}

	/*
	 * Initializes the SPI driver 2. The SPI2 signals are routed as follow:
	 * PB12 - NSS.
	 * PB13 - SCK.
	 * PB14 - MISO.
	 * PB15 - MOSI.
	 */
//	spiStart(&SPID2, &spi2cfg);
//	palSetPad(GPIOB, 12);
//	palSetPadMode(GPIOB, 12,
//			PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
//	/* NSS.     */
//	palSetPadMode(GPIOB, 13, PAL_MODE_ALTERNATE(5) | PAL_STM32_OSPEED_HIGHEST);
//	/* SCK.     */
//	palSetPadMode(GPIOB, 14, PAL_MODE_ALTERNATE(5));
//	/* MISO.    */
//	palSetPadMode(GPIOB, 15, PAL_MODE_ALTERNATE(5) | PAL_STM32_OSPEED_HIGHEST);
//	/* MOSI.    */
	/*
	 * Initializes the SPI driver 1 in order to access the MEMS. The signals
	 * are initialized in the board file.
	 * Several LIS302DL registers are then initialized.
	 */
//	spiStart(&SPID1, &spi1cfg);
}
